package com.link.base.base.interstatistics.service;

import com.link.base.base.interstatistics.dao.mybatis.mapper.VisitInfoMapper;
import com.link.base.base.interstatistics.model.ReportInfo;
import com.link.base.base.interstatistics.model.ReportParam;
import com.link.base.base.interstatistics.model.VisitInfo;
import com.link.base.base.interstatistics.model.VisitStatistics;
import com.link.base.base.urlinfo.dao.mybatis.mapper.UrlInfoMapper;
import com.link.core.basic.dao.mapper.BasicMapper;
import com.link.core.basic.service.BasicServiceImpl;
import com.link.core.basic.service.ServiceException;
import com.link.core.cllog.LCLogger;
import com.link.core.util.RedisUtil;
import com.link.core.util.StringUtils;
import com.link.core.util.redisclient.LinkRedisClient;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author: 王艳
 * @date: 2018-12-13
 * @comments:
 */
@Service
public class VisitInfoServiceImpl extends BasicServiceImpl<VisitInfo> implements VisitInfoService {

    @Resource
    private VisitInfoMapper visitInfoMapper;

    @Resource
    private UrlInfoMapper urlInfoMapper;

    @Override
    public BasicMapper<VisitInfo> getBasicMapper() {
        return visitInfoMapper;
    }

    /**
     * 今日访问量
     */
    private static int todayVisitNumInLog = 0;
    /**
     * 今日越权量
     */
    private static int todayAuthNumInLog = 0;
    /**
     * 本月访问量
     */
    private static int thisMonthVisitNumInLog = 0;
    /**
     * 本月越权量
     */
    private static int thisMonthAuthNumInLog = 0;
    /**
     * 总计访问量
     */
    private static int totalVisitNumInLog = 0;
    /**
     * 总计越权量
     */
    private static int totalAuthNumInLog = 0;

    /**
     * @param visitInfo
     * @return
     * @author Dai chenyu
     */
    @Override
    public List<ReportInfo> queryReportOfInterfaceVisit(VisitInfo visitInfo) {
        List<ReportInfo> res = new ArrayList<>();
        List<ReportInfo> queryRes = null;
        ReportParam query = new ReportParam();
        query.setUrl(visitInfo.getQueryReportUrl());
        ReportInfo info = new ReportInfo();
        try {
            //查询出输入url总计的访问数
            query.setVisitType("Visit");
            queryRes = visitInfoMapper.getCountRes(query);
            if (queryRes != null && queryRes.size() > 0) {
                info.setTotalVisitNumInLog(queryRes.get(0).getNum());
            }
            //查询出输入url总计的越权数
            queryRes = null;
            query.setVisitType("Over");
            queryRes = visitInfoMapper.getCountRes(query);
            if (queryRes != null && queryRes.size() > 0) {
                info.setTotalAuthNumInLog(queryRes.get(0).getNum());
            }
            //查询出输入url本日的访问数
            queryRes = null;
            query.setQueryDateBegin(getTodayStart());
            query.setQueryDateEnd(getNextDayStart());
            query.setVisitType("Visit");
            queryRes = visitInfoMapper.getCountRes(query);
            if (queryRes != null && queryRes.size() > 0) {
                info.setTodayVisitNumInLog(queryRes.get(0).getNum());
            }
            //查询出输入url本日的越权数
            queryRes = null;
            query.setQueryDateBegin(getTodayStart());
            query.setQueryDateEnd(getNextDayStart());
            query.setVisitType("Over");
            queryRes = visitInfoMapper.getCountRes(query);
            if (queryRes != null && queryRes.size() > 0) {
                info.setTodayAuthNumInLog(queryRes.get(0).getNum());
            }
            //查询出输入url本月的访问数
            queryRes = null;
            query.setQueryDateBegin(getMonthBegin());
            query.setQueryDateEnd(getNextMonthBegin());
            query.setVisitType("Visit");
            queryRes = visitInfoMapper.getCountRes(query);
            if (queryRes != null && queryRes.size() > 0) {
                info.setThisMonthVisitNumInLog(queryRes.get(0).getNum());
            }
            //查询出输入url本月的越权数
            queryRes = null;
            query.setQueryDateBegin(getMonthBegin());
            query.setQueryDateEnd(getNextMonthBegin());
            query.setVisitType("Over");
            queryRes = visitInfoMapper.getCountRes(query);
            if (queryRes != null && queryRes.size() > 0) {
                info.setThisMonthAuthNumInLog(queryRes.get(0).getNum());
            }
            //计算获得真实访问量，访问量-越权量
            info.setTodayNumInLog(info.getTodayVisitNumInLog() - info.getTodayAuthNumInLog());
            info.setThisMonthNumInLog(info.getThisMonthVisitNumInLog() - info.getThisMonthAuthNumInLog());
            info.setTotalNumInLog(info.getTotalVisitNumInLog() - info.getTotalAuthNumInLog());

            res.add(info);
        } catch (Exception e) {
            LCLogger.error().withMessageKey("queryReportOfInterfaceVisit").withMessage("数据库查询报表内容错误:" + e.getMessage());
        }

        return res;
    }

    /**
     * @return
     * @author Dai Chenyu
     */
    @Override
    public Map<String, List<ReportInfo>> queryReportOfTop10(ReportParam param) {
        Map<String, List<ReportInfo>> map = new HashMap<>(9);
        ReportParam query = new ReportParam();
        try {
            //访问量
            List<ReportInfo> visitNumber;
            //越权访问量
            List<ReportInfo> authNumber;
            //正常访问量
            List<ReportInfo> normalNumber;
            //数据篡改量
            List<ReportInfo> tamper;

            //接口访问耗时
            //定义排序规则
            Comparator<ReportInfo> comparable = new Comparator<ReportInfo>() {
                @Override
                public int compare(ReportInfo o1, ReportInfo o2) {
                    if (o1.getNum() < o2.getNum()) {
                        return 1;
                    }
                    if (o1.getNum() == o2.getNum()) {
                        return 0;
                    }
                    return -1;
                }
            };


            //前端传时间区间，根据时间区间查top10
            if (param.getQueryDateBegin() != 0) {
                query.setQueryDateBegin(param.getQueryDateBegin());
            }
            if (param.getQueryDateEnd() != 0) {
                query.setQueryDateEnd(param.getQueryDateEnd());
            }
            query.setRecordCount(param.getRecordCount());
            //查询接口消耗时间排行，默认为top10
            if (query.getRecordCount() <= 0.0 ) {
                query.setRecordCount(10);
            }
            List<ReportInfo> reqAvgConsumeTime = visitInfoMapper.getReqAvgConsumeTime(query);
            map.put("reqAvgConsumeTime",reqAvgConsumeTime);

            visitNumber = visitInfoMapper.getCountRes(query);

            query.setVisitType("Over");
            authNumber = visitInfoMapper.getCountRes(query);
            query.setVisitType("Visit");
            normalNumber = visitInfoMapper.getCountRes(query);
            query.setVisitType("DataChange");
            tamper = visitInfoMapper.getCountRes(query);
            //排序获得访问数的top10
            Collections.sort(visitNumber, comparable);
            int ten = 10;
            if (visitNumber.size() > ten) {
                map.put("visitNumber", visitNumber.subList(0, 10));
            } else {
                map.put("visitNumber", visitNumber);
            }

            //排序获得越权数的top10
            Collections.sort(authNumber, comparable);
            if (authNumber.size() > ten) {
                map.put("authNumber", authNumber.subList(0, 10));
            } else {
                map.put("authNumber", authNumber);
            }

            //排序获得正常访问数的top10
            Collections.sort(normalNumber, comparable);
            if (normalNumber.size() > ten) {
                map.put("normalNumber", normalNumber.subList(0, 10));
            } else {
                map.put("normalNumber", normalNumber);
            }

            //排序获得篡改数的top10
            Collections.sort(tamper, comparable);
            if (tamper.size() > ten) {
                map.put("tamper", tamper.subList(0, 10));
            } else {
                map.put("tamper", tamper);
            }
        } catch (Exception e) {
            LCLogger.error().withMessageKey("queryReportOfInterfaceVisit").withMessage("数据库查询报表内容错误:" + e.getMessage());
        }

        return map;
    }

    /**
     * @return
     * @author Dai Chenyu
     */
    @Override
    public Map<String, int[]> queryReportByDate(String date) {
        Map<String, int[]> res = new HashMap<>();
        try {
            //获得当前日期0点的时间戳
            long start = date2TimeStamp(date);
            //获得当前日期24点的时间戳
            long end = start + 86400000;
            ReportParam query = new ReportParam();
            query.setQueryDateBegin(start);
            query.setQueryDateEnd(end);
            //如果查询日期是当天，将缓存中数据写入数据库
            Date day = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            if (start == date2TimeStamp(sdf.format(day))) {
                LinkRedisClient jedis = null;
                flushAllByCache(jedis);
            }
            //查询出当前日期内的访问数据
            List<VisitStatistics> visitStatistics = visitInfoMapper.queryVisitStatistics(query);
            for (VisitStatistics v : visitStatistics) {
                if (!res.keySet().contains(v.getUrl())) {
                    int[] array = new int[24];
                    double time = (double) v.getVisitTime().longValue() - start;
                    int index = (int) Math.floor(time / 3600000) - 1;
                    array[index]++;
                    res.put(v.getUrl(), array);
                } else {
                    double time = (double) v.getVisitTime().longValue() - start;
                    int index = (int) Math.floor(time / 3600000) - 1;
                    int[] array = res.get(v.getUrl());
                    array[index]++;
                }
            }
        } catch (Exception e) {
            LCLogger.error().withMessageKey("queryReportByDate").withMessage(e.getMessage());
        }

        return res;
    }

    /**
     * @return
     * @author Dai Chenyu
     */
    @Override
    public Map<String, Map<String, Integer>> queryReportByDateRange(long start, long end) {
        Map<String, int[]> res = new HashMap<>();
        Map<String, Map<String, Integer>> result = new HashMap<>();
        try {
            ReportParam query = new ReportParam();
            query.setQueryDateBegin(start);
            query.setQueryDateEnd(end);
            long block = end - start;
            //查询出日期区间内的访问数据
            List<VisitStatistics> visitStatistics = visitInfoMapper.queryVisitStatistics(query);
            for (VisitStatistics v : visitStatistics) {
                //时间区间平分为36个区间统计
                if (!res.keySet().contains(v.getVisitType())) {
                    int[] array = new int[36];
                    long time = v.getVisitTime().longValue() - start;
                    int index = (int) (array.length * time / block);
                    array[index]++;
                    res.put(v.getVisitType(), array);
                } else {
                    int[] array = res.get(v.getVisitType());
                    long time = v.getVisitTime().longValue() - start;
                    int index = (int) (array.length * time / block);
                    array[index]++;
                }
            }
            //结果转换为 时间-统计数量 的格式返回给前端
            for (String type : res.keySet()) {
                int[] arr = res.get(type);
                Map<String, Integer> timeMap = new LinkedHashMap<>();
                for (int i = 0; i < arr.length; i++) {
                    timeMap.put(timeStamp2Date(start + block * (i + 1) / 36), arr[i]);
                }
                result.put(type, timeMap);
            }
        } catch (Exception e) {
            LCLogger.error().withMessageKey("queryReportByDate").withMessage(e.getMessage());
        }

        return result;
    }

    /**
     * 解析redis未推送的数据生成报表
     *
     * @param visitInfo
     * @return
     */
    private void processReport(VisitInfo visitInfo, ReportInfo reportInfo) throws Exception {
//        LinkRedisClient jedis = RedisUtil.getJedis();
//        //获取redis未推送的数据
//        int redisIndexDB = VisitStatisticsTools.getDbIndex(reportInfo.getUrl());
//        //jedis.select(redisIndexDB);
//        LCLogger.info().withMessageKey("queryReportOfInterfaceVisit").withMessage("################选择" + redisIndexDB + "第个库##############");
//        String visitStr = jedis.hget(VisitStatisticsConstants.VISIT_STATISTICS_KEY, visitInfo.getQueryReportUrl());
//        JSONArray jedisArray = JSONObject.parseObject(visitStr).getJSONArray("message");
//        //计算redis中未推送的访问量
//        for (int i = 0; i < jedisArray.size(); i++) {
//            VisitStatisticsCache vs = JSONObject.parseObject(jedisArray.get(i).toString(), VisitStatisticsCache.class);
//            List<String> visitTimestamps = vs.getVisitTimestamps();
//            List<String> overAuthorityTimestamps = vs.getOverAuthorityTimestamp();
//            getVisitNum(visitTimestamps, overAuthorityTimestamps);
//        }
//        reportInfo.setTodayVisitNumInLog(reportInfo.getTodayVisitNumInLog() + todayVisitNumInLog);
//        reportInfo.setTodayAuthNumInLog(reportInfo.getTodayAuthNumInLog() + todayAuthNumInLog);
//        reportInfo.setThisMonthVisitNumInLog(reportInfo.getThisMonthVisitNumInLog() + thisMonthVisitNumInLog);
//        reportInfo.setThisMonthAuthNumInLog(reportInfo.getThisMonthAuthNumInLog() + thisMonthAuthNumInLog);
//        reportInfo.setTotalVisitNumInLog(reportInfo.getTotalVisitNumInLog() + totalVisitNumInLog);
//        reportInfo.setTotalAuthNumInLog(reportInfo.getTotalAuthNumInLog() + totalAuthNumInLog);
    }

    public static long getTodayStart() {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTimeInMillis();
    }

    public static long getNextDayStart() {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTimeInMillis();
    }

    public static long getMonthBegin() {
        Calendar cal = Calendar.getInstance();
        cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONDAY), cal.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH));
        return cal.getTimeInMillis();
    }

    public static long getNextMonthBegin() {
        Calendar cal = Calendar.getInstance();
        cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONDAY), cal.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
        cal.set(Calendar.HOUR_OF_DAY, 23);
        return cal.getTimeInMillis();
    }

    public static void main(String[] args) {
        System.out.println("todayBegin:" + getTodayStart());
        System.out.println("nextDayBegin:" + getNextDayStart());
        System.out.println("thisMonthBegin:" + getMonthBegin());
        System.out.println("nextMonthBegin:" + getNextMonthBegin());
    }

    public static String timeStamp2Date(long timeStamp) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String sd = sdf.format(new Date(timeStamp));
        return sd;
    }

    public static long date2TimeStamp(String s) throws Exception {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date = simpleDateFormat.parse(s);
        long ts = date.getTime();
        return ts;
    }

    @Override
    public void flushAllByCache(LinkRedisClient jedis) throws Exception {
        Set<String> keys = jedis.keys("{rate.limit:}*");
        jedis.del(keys);
    }

    @Override
    public void flushAllByDb(LinkRedisClient jedis) throws Exception {
    }

    /**
     * 计算访问量和越权量
     *
     * @param visitTimestamps
     * @param overAuthorityTimestamps
     */
    private void getVisitNum(List<String> visitTimestamps, List<String> overAuthorityTimestamps) {
        long todayBegin = getTodayStart();
        long nextDayBegin = getNextDayStart();
        long thisMonthBegin = getMonthBegin();
        long nextMonthBegin = getNextMonthBegin();
        //计算访问量
        if (visitTimestamps != null) {
            for (int j = 0; j < visitTimestamps.size(); j++) {
                //如果有就总数+1
                totalVisitNumInLog++;
                //获取记录中的访问时间
                String visitTimestamp = visitTimestamps.get(j).split("-")[0];
                long visitTime = Long.parseLong(visitTimestamp);
                //如果在本月，则本月数量+1
                if (visitTime >= thisMonthBegin && visitTime < nextMonthBegin) {
                    thisMonthVisitNumInLog++;
                    //如果在本日，则本日数量+1
                    if (visitTime >= todayBegin && visitTime < nextDayBegin) {
                        todayVisitNumInLog++;
                    }
                }
            }
        }

        if (overAuthorityTimestamps != null) {
            //计算越权量
            for (int j = 0; j < overAuthorityTimestamps.size(); j++) {
                //如果有就总数+1
                totalAuthNumInLog++;
                //获取记录中的访问时间
                String overAuthorityTimestamp = overAuthorityTimestamps.get(j).split("-")[0];
                long visitTime = Long.parseLong(overAuthorityTimestamp);
                //如果在本月，则本月数量+1
                if (visitTime >= thisMonthBegin && visitTime < nextMonthBegin) {
                    thisMonthAuthNumInLog++;
                    //如果在本日，则本日数量+1
                    if (visitTime >= todayBegin && visitTime < nextDayBegin) {
                        todayAuthNumInLog++;
                    }
                }
            }
        }
    }

    /**
     * 将访问历史记录写入数据库
     *
     * @param list
     * @throws Exception
     */
    @Override
    public void insertVisitStatistics(List<VisitStatistics> list) throws Exception {
        visitInfoMapper.insertVisitStatistics(list);
    }

    @Override
    public void flushRecordInCache(VisitInfo visitInfo) throws Exception {
        LinkRedisClient jedis = null;
        try {
            jedis = RedisUtil.getJedis();
            String url = visitInfo.getQueryReportUrl().trim();
            if (!StringUtils.isBlank(url)) {
                flushRecord(jedis, url);
            }
            List<String> urls = visitInfo.getUrls();
            if (null != urls && !urls.isEmpty()) {
                for (int i = 0; i < urls.size(); i++) {
                    flushRecord(jedis, urls.get(i).trim());
                }
            }
        } catch (Exception e) {
            LCLogger.withException(e);
            throw new ServiceException("INTERSTATIS-001");
        } finally {
            RedisUtil.returnResource(jedis);
        }
    }

    public void flushRecord(LinkRedisClient jedis, String url) throws Exception {
    }

}

